package com.demo.config.simphash;

import org.apache.commons.lang3.StringUtils;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

// 字符串查重率计算
public class SimHashUtil {

	public static void main(String[] args) {
		// System.out.println("上飞机,似懂非懂,似23懂非懂");
		// System.out.println(checkDuplicateRate("上飞机,似懂非懂,似23懂非懂"));

	}

	/**
	 * 字符串查重率计算
	 **/
	public static double checkDuplicateRate(String targetStr) {
		// StringUtils.isBlankAssert(targetStr, "查重目标字符串不能为空");
		// ResultBean resultBean = new ResultBean();

		// 1.查询当前所有项目名称数据
		List<String> projectNameList = List.of("上飞机", "似懂非懂", "似23懂非懂");

		// 2.先对名称和基础数据进行预处理
		String finalTargetStr = StringUtils.replaceAll(targetStr, "[^a-zA-Z0-9\\u4E00-\\u9FA5]", "").toLowerCase();
		List<String> processedList = projectNameList.stream()
				.map(str -> StringUtils.replaceAll(str, "[^a-zA-Z0-9\\u4E00-\\u9FA5]", "").toLowerCase())
				.collect(Collectors.toList());

		// 3.使用算法对基础数据循环比对
		AtomicReference<Double> maxSimilarity = new AtomicReference<>(0.0);
		processedList.parallelStream().forEach(name -> {
			double sim = similarity(finalTargetStr, name, false);
			maxSimilarity.updateAndGet(current -> Double.compare(sim, current) > 0 ? sim : current);
		});

		// 格式化保留两位小数
		DecimalFormat df = new DecimalFormat("0.00"); // 保留两位小数
		double roundedValue = Double.parseDouble(df.format(maxSimilarity.get())) * 100; // 获取保留两位小数后的值
																						// 并转换百分比

		// 4.取相似度最高值最终结果
		// resultBean.setSuccess();
		// resultBean.addParam("maxSimilarity", roundedValue);
		return roundedValue;
	}

	/**
	 * 计算相似度工具方法 莱文斯坦距离算法来计算两个字符串之间的相似度
	 **/
	public static double similarity(String strA, String strB, boolean isRegex) {
		// 移除字符串中的非字母、数字、汉字部分
		if (isRegex) {
			strA = strA.replaceAll("[^a-zA-Z0-9\\u4E00-\\u9FA5]", "");
			strB = strB.replaceAll("[^a-zA-Z0-9\\u4E00-\\u9FA5]", "");
		}

		// 计算莱文斯坦距离
		int[][] distance = new int[strA.length() + 1][strB.length() + 1];
		for (int i = 0; i <= strA.length(); i++) {
			distance[i][0] = i;
		}
		for (int j = 0; j <= strB.length(); j++) {
			distance[0][j] = j;
		}
		for (int i = 1; i <= strA.length(); i++) {
			for (int j = 1; j <= strB.length(); j++) {
				if (strA.charAt(i - 1) == strB.charAt(j - 1)) {
					distance[i][j] = distance[i - 1][j - 1];
				}
				else {
					distance[i][j] = Math.min(distance[i - 1][j] + 1,
							Math.min(distance[i][j - 1] + 1, distance[i - 1][j - 1] + 1));
				}
			}
		}

		// 计算相似度
		int maxLen = Math.max(strA.length(), strB.length());
		return (maxLen - distance[strA.length()][strB.length()]) / (double) maxLen;
	}

	/**
	 * 检验文本查重率
	 **/
	public static double calculateDuplicationRate(String ServiceContext, String targetStr) {
		// StringUtils.isBlankAssert(targetStr, "查重目标字符串不能为空");

		// 1.查询当前所有项目名称数据
		List<String> abstractStrList = List.of("如图", "oiuhgh0", "全额堵车");
		// 2. 根据标点符号进行断句 此处原计划采用三方分词工具暂时按照标点符号断句
		Set<String> finalTargetStrSet = splitSentences(targetStr);
		Set<Set<String>> processedSet = splitSentences(new HashSet<>(abstractStrList));
		// todo 考虑将processedSet结果放入缓存 设置过期时间 避免重复查数据库
		// 3.使用算法对两组文本基础数据循环比对
		AtomicReference<Double> maxSimilarity = new AtomicReference<>(0.0);
		processedSet.parallelStream().forEach(set -> {
			double sim = calculateJaccardSimilarity(finalTargetStrSet, set);
			maxSimilarity.updateAndGet(current -> Double.compare(sim, current) > 0 ? sim : current);
		});
		// 格式化保留两位小数
		DecimalFormat df = new DecimalFormat("0.00"); // 保留两位小数
		double roundedValue = Double.parseDouble(df.format(maxSimilarity.get())) * 100; // 获取保留两位小数后的值
																						// 并转换百分比
		// 4.取相似度最高值最终结果
		// resultBean.setSuccess();
		// resultBean.addParam("maxSimilarity",roundedValue);
		return roundedValue;
	}

	/**
	 * 文本字符串根据标点符号分句
	 **/
	public static Set<String> splitSentences(String text) {
		// 定义正则表达式，用于匹配句子的标点符号
		String regex = "[。！？，；：“”‘’【】《》（）\\[\\]{}.,;:\"'?!]";

		// 使用正则表达式将字符串分句
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(text);
		int start = 0;
		Set<String> sentences = new HashSet<>();

		while (matcher.find()) {
			String sentence = text.substring(start, matcher.start()).trim();
			if (!sentence.isEmpty()) {
				sentences.add(sentence.toLowerCase().replaceAll("\\s+", ""));
			}
			start = matcher.end();
		}

		// 处理最后一个句子
		String lastSentence = text.substring(start).trim();
		if (!lastSentence.isEmpty()) {
			sentences.add(lastSentence.toLowerCase().replaceAll("\\s+", ""));
		}

		return sentences;
	}

	/**
	 * 文本分句工具方法 文本集合根据标点符号分句
	 **/
	public static Set<Set<String>> splitSentences(Set<String> inputSet) {
		return inputSet.stream()
				.map(str -> Arrays.stream(str.split("[。！？，；：“”‘’【】《》（）\\[\\]{}.,;:\"'?!]")).map(String::toLowerCase)
						.map(sentence -> sentence.replaceAll("\\s+", ""))// 去除空格
						.collect(Collectors.toSet()))
				.collect(Collectors.toSet());
	}

	/**
	 * Jaccard计算相似度方法 Jaccard计算相似度 double
	 **/
	public static double calculateJaccardSimilarity(Set<String> set1, Set<String> set2) {
		if (set1.size() == 0 && set2.size() == 0) {
			return 1.0;
		}
		// 都为空相似度为 1
		if (set1.size() == 0 || set2.size() == 0) {
			return 0.0;
		}
		Set<String> intersection = new HashSet<>(set1);
		intersection.retainAll(set2); // 计算交集

		Set<String> union = new HashSet<>(set1);
		union.addAll(set2); // 计算并集

		return (double) intersection.size() / union.size();
	}

}
